// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CasVideoEngine.h"
#include "CasLog.h"
#include "CasDecodeController.h"

namespace {
    const uint32_t DATA_LENGTH_MAX = 10 * 1024 * 1024;
}

/*
 * @fn CasVideoEngine
 * @brief constructor
 */
CasVideoEngine::CasVideoEngine() = default;

/*
 * @fn CasVideoEngine
 * @brief destructor
 */
CasVideoEngine::~CasVideoEngine() = default;

/*
 * @fn InitDecoder
 * @brief initialisation interface
 * @param[in] nativeWindow View to display of (type <tt>ANativeWindow*</tt>)
 * @param[in] type Method to decode, fixed to DECODER_TYPE_HW, of (type <tt>enum DecoderType</tt>)
 * @param[in] rotation degrees
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_INIT_FAIL
 *                VIDEO_ENGINE_CLIENT_PARAM_INVALID
 *                VIDEO_ENGINE_CLIENT_PARAM_UNSUPPORTED
 */
uint32_t CasVideoEngine::InitDecoder(ANativeWindow *nativeWindow, DecoderType type, FrameType frameType, int rotationDegrees)
{
    std::lock_guard<std::mutex> lockGuard(m_lock);
    if (nativeWindow == nullptr) {
        ERR("ANativeWindow Nullptr.");
        return VIDEO_ENGINE_CLIENT_PARAM_INVALID;
    }
    if (type != DecoderType::DECODER_TYPE_HW) {
        ERR("Unsupported DecoderType.");
        return VIDEO_ENGINE_CLIENT_PARAM_UNSUPPORTED;
    }
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return VIDEO_ENGINE_CLIENT_INIT_FAIL;
    }
    return decodeController->Init(nativeWindow, frameType, rotationDegrees);
}

/*
 * @fn StartDecoder
 * @brief start interface
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_START_ERR
 */
uint32_t CasVideoEngine::StartDecoder()
{
    std::lock_guard<std::mutex> lockGuard(m_lock);
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return VIDEO_ENGINE_CLIENT_START_ERR;
    }
    return decodeController->Start();
}

/*
 * @fn DecodeFrame
 * @brief decode interface
 * @param[in] buf The initial address of current frame of (type <tt>int8_t*</tt>)
 * @param[in] length The length of current frame of (type <tt>size_t</tt>)
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_DECODE_ERR
 *                VIDEO_ENGINE_CLIENT_PARAM_INVALID
 */
uint32_t CasVideoEngine::DecodeFrame(uint8_t *buf, size_t length)
{
    std::lock_guard<std::mutex> lockGuard(m_lock);
    if (buf == nullptr || length == 0) {
        ERR("No Input Data.");
        return VIDEO_ENGINE_CLIENT_PARAM_INVALID;
    }
    if (length > DATA_LENGTH_MAX) {
        ERR("Exceed max data length");
        return VIDEO_ENGINE_CLIENT_PARAM_INVALID;
    }
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return VIDEO_ENGINE_CLIENT_DECODE_ERR;
    }
    return decodeController->Decode(buf, length);
}

/*
 * @fn：StopDecoder
 * @brief：stop interface
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_STOP_ERR
 */
uint32_t CasVideoEngine::StopDecoder()
{
    std::lock_guard<std::mutex> lockGuard(m_lock);
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return VIDEO_ENGINE_CLIENT_STOP_ERR;
    }
    return decodeController->Stop();
}

/*
 * @fn DestroyDecoder
 * @brief destroy interface
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_DESTROY_ERR
 */
uint32_t CasVideoEngine::DestroyDecoder()
{
    std::lock_guard<std::mutex> lockGuard(m_lock);
    return CasDecodeController::DestroyInstance();
}

/*
 * @fn GetDecoderStatus
 * @brief get status interface
 * @param[out] stat The status of the engine decoder, pass by reference, of (type <tt>enum EngineStat</tt>)
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_GET_STATUS_ERR
 */
uint32_t CasVideoEngine::GetDecoderStatus(EngineStat &stat)
{
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return VIDEO_ENGINE_CLIENT_GET_STATUS_ERR;
    }
    stat = decodeController->GetStatus();
    return SUCCESS;
}

/*
 * @fn GetDecoderStatics
 * @brief get statistics interface
 * @param[out] stat The statistics of the engine decoder, pass by reference, of (type <tt>struct DecoderStatics</tt>)
 * @return errno: SUCCESS
 *                VIDEO_ENGINE_CLIENT_GET_STAT_ERR
 */
uint32_t CasVideoEngine::GetDecoderStatistics(DecoderStatistics &statistics)
{
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return VIDEO_ENGINE_CLIENT_GET_STAT_ERR;
    }
    statistics = decodeController->GetStatistics();
    return SUCCESS;
}

void CasVideoEngine::SetFirstVidFrameListener(CasFirstVideoFrameListener *listener)
{
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return;
    }
    decodeController->SetFirstFrameListener(listener);
}

void CasVideoEngine::SetVideoDecodeStatListener(CasVideoDecodeStatListener *listener)
{
    CasDecodeController *decodeController = CasDecodeController::GetInstance();
    if (decodeController == nullptr) {
        ERR("Failed to instantiate.");
        return;
    }
    decodeController->SetDecodeStatListener(listener);
}